/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  SPI MODE 3
		change data @negedge
		read data @posedge

 RSTB-active low asyn reset, CLK-clock, T_RB=0-rx  1-TX, mlb=0-LSB 1st 1-msb 1st
 START=1- starts data transmission cdiv 0=clk/64 1=/128 2=/256  3=/512
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
module spi_master(
     input rstb
    ,input clk
    ,input mlb
    ,input start
    ,input [7:0] taddr  //transmit addr
    ,input [7:0] tdat   //transmit data
    ,input [1:0] cdiv   //clock divider
    ,input din 
    ,output reg ss
    ,output reg sck
    ,output     dout
    ,output reg done
    ,output reg [7:0] rdata  //received data
);

parameter IDLE=2'b00;		
parameter SEND=2'b10; 
parameter FINISH=2'b11; 
reg [1:0] curt, next;

reg [16 -1: 0] treg;
reg [8 -1: 0] rreg;
reg [6 -1:0] nbit;
reg [24 -1:0] mid,cnt;
reg shift;
reg clr;

//FSM i/o
always @(*)
begin
  next = curt;
  case(curt)
    IDLE: begin
      if(start==1) begin 
        next = SEND;	 
      end
    end //IDLE
    SEND: begin
      if ((nbit>=32) && (cnt == mid-1)) begin
      	next = FINISH;
      end
    end //SEND
    FINISH:begin
      next = IDLE;
    end //FINISH
    default: next = IDLE;
  endcase
end//always

//state transistion
always @ (posedge clk or negedge rstb)
begin
  if(rstb==0) 
    curt <= IDLE;
  else 
    curt <= next;
end

//
always @ (posedge clk or negedge rstb) 
begin
  if(!rstb)begin
    done <= 1'b0;
  end else if (curt == FINISH) begin
    done <= 1'b1;
  end else begin
    done <= 1'b0;
  end
end
//
always @ (posedge clk or negedge rstb) 
begin
  if(!rstb)begin
    clr <= 1'b1;
  end else if (curt == IDLE) begin
    clr <= 1'b1;
  end else begin
    clr <= 1'b0;
  end
end
//
always @ (posedge clk or negedge rstb) 
begin
  if(!rstb)begin
    shift <= 0;
  end else if ((curt == IDLE) || (curt == SEND))begin
    shift <= 1;
  end else if (curt == FINISH) begin
    shift <= 0;
  end else begin
    shift <= 0;
  end
end

always @ (posedge clk or negedge rstb) 
begin
  if (!rstb) begin
    ss <= 1;
  end else if (curt == SEND) begin
    ss <= 0;
  end else begin
    ss <= 1;
  end
end

always @ (posedge clk or negedge rstb) 
begin
  if (!rstb) begin
    rdata <= 0;
  end else if (curt == FINISH) begin
    rdata <= rreg;
  end else begin
    rdata <= 0;
  end
end

always @ (posedge clk or negedge rstb) 
begin
  if (!rstb) begin
    mid <= 256;
  end else if (curt == IDLE) begin
    case (cdiv)
      2'b00: mid <= 32;
      2'b01: mid <= 64;
      2'b10: mid <= 128;
`ifdef DEBUG
      2'b11: mid <= 8;
`else
      2'b11: mid <= 300000;
`endif
    endcase
  end
end

//setup falling edge (shift dout) sample rising edge (read din)
always @ (posedge clk or negedge rstb) //NEGEDGE
begin
  if(!rstb)begin
    cnt <= 0; sck <= 1;
  end else if (clr == 1) begin
    cnt <= 0; sck <= 1;
  end else begin
	if (shift == 1) begin
	  cnt <= cnt + 1; 
	  if((cnt == mid)&&(curt == SEND))begin
	  	sck <= ~sck;
		cnt <= 0;
	  end //mid
    end //shift
  end //rst
end //always

always @ (posedge clk or negedge rstb) //NEGEDGE
begin
  if(!rstb)begin
    nbit <= 0;
  end else if (clr == 1) begin
    nbit <= 0; 
  end else begin
	if (shift == 1) begin
	  if(cnt == mid) begin
        nbit <= nbit+1;
	  end //mid
    end //shift
  end //rst
end //always

//sample @ rising edge (read din)
always @ (posedge clk or negedge rstb)
begin
  if(!rstb)begin
    rreg <= 8'hFF;
  end else if (clr == 1) begin
    rreg <= 8'hFF;
  end else if ((nbit[0] == 1) && (cnt == mid))begin 
    if (mlb==0) begin //LSB first, din@msb -> right shift
      rreg <= {din,rreg[7:1]};
    end else begin //MSB first, din@lsb -> left shift
	  rreg <= {rreg[6:0],din};
    end
  end //rst
end //always

always @ (posedge clk or negedge rstb) //NEGEDGE
begin
  if(!rstb)begin
    treg <= 16'hFFFF; //dout <= 1'b1;  
  end else if(clr == 1) begin
    treg <= 16'hFFFF; //dout <= 1'b1;  
  end else if ((nbit[0] == 0) && (cnt == mid))begin
    if(nbit == 0) begin //load data into TREG
  	  treg <= {tdat,taddr};
    end else begin
      if (mlb == 0) begin //LSB first, shift right
  	    treg <= {1'b1, treg[15: 1]};
//        dout <= treg[0];
      end else begin //MSB first shift LEFT
  	    treg <= {treg[14: 0], 1'b1};
//        dout <= treg[15];
      end
  	end
  end //rst
end //always

assign dout = mlb ? treg[15] : treg[0];

endmodule
